package project.auth.jaas;

import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import project.svc.auth.Authentication;
import core.config.Env;
import core.config.Factory;

/**
 * ログオンモジュール
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class LogonModule implements LoginModule {
	/** ログイン情報 ユーザIDプロンプト */
	private static final String ENV_PROMPT_UID = "Jaas.UsrPrompt";
	/** ログイン情報 パスワードプロンプト */
	private static final String ENV_PROMPT_PWD = "Jaas.PwdPrompt";

	/** サブジェクトオブジェクト */
	private Subject sbj = null;
	/** コールバックハンドラ */
	private CallbackHandler ch = null;
	/** Sheared State */
//	private Map ss;
	/** Options */
//	private Map opt;
	/** ログオン状態 */
	private boolean logon = false;
	/** コミット状態 */
	private boolean commit = false;
	/** ログオンプリンシパル */
	private LogonPrincipal lp = null;
	/** ユーザID */
	private String uid = null;

	/**
	 * 初期化
	 *
	 * @param subject サブジェクト
	 * @param callback コールバック
	 * @param sharedState 共有状態
	 * @param options オプション
	 */
	@Override
	public void initialize(final Subject subject, final CallbackHandler callback,
					final Map<String, ?> sharedState, final Map<String, ?> options) {
		this.sbj = subject;
		this.ch = callback;
//		this.ss = sharedState;
//		this.opt = options;

		this.logon = false;
		this.commit = false;
	}

	/**
	 * ログイン
	 *
	 * @return ログオン状態
	 * @throws LoginException ログイン例外
	 */
	@Override
	public boolean login() throws LoginException {
		if (this.ch == null) {
			throw new LoginException("CallbackHandler is null.");
		}

		String prompt = Env.getEnv(ENV_PROMPT_UID, "user name: ");
		NameCallback nc = new NameCallback(prompt);

		prompt = Env.getEnv(ENV_PROMPT_PWD, "password: ");
		PasswordCallback pc = new PasswordCallback(prompt, false);

		Callback[] callbacks = {nc, pc};

		try {
			// UID取得
			this.ch.handle(callbacks);
			this.uid = nc.getName();
			if (this.uid != null) {
				this.uid = this.uid.trim();
			}

			// PWD取得
			char[] tmp = pc.getPassword();
			if (tmp == null) {
				tmp = new char[0];
			}
			String pwd = new String(tmp);
			pc.clearPassword();

			Authentication auth = Factory.create(Authentication.class);
			this.logon = auth.logon(this.uid, pwd);
			if (!this.logon) {
				this.uid = null;
			}

			return this.logon;
		} catch (final IOException | UnsupportedCallbackException ex) {
			throw new LoginException(ex.getMessage());
		}
	}

	/**
	 * コミット処理
	 *
	 * @return コミット状態
	 * @throws LoginException ログイン例外
	 */
	@Override
	public boolean commit() throws LoginException {
		this.commit = this.logon;

		if (this.logon) {
			this.lp = new LogonPrincipal(this.uid);
			if (!this.sbj.getPrincipals().contains(this.lp)) {
				this.sbj.getPrincipals().add(this.lp);
			}
			this.uid = null;
		}
		return this.commit;
	}

	/**
	 * アボート処理
	 *
	 * @return 常にfalse
	 * @throws LoginException ログイン例外
	 */
	@Override
	public boolean abort() throws LoginException {
		if (this.logon) {
			if (this.commit) {
				logout();
			} else {
				this.logon = false;
				this.uid = null;
				this.lp = null;
			}
		}
		return false;
	}

	/**
	 * ログアウト処理
	 *
	 * @return 常にtrue
	 * @throws LoginException ログイン例外
	 */
	@Override
	public boolean logout() throws LoginException {
		this.sbj.getPrincipals().remove(this.lp);
		this.logon = false;
		this.commit = false;
		this.uid = null;
		this.lp = null;

		return true;
	}
}
